package com.ae.shield.common.recipe.assembly;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.ae.shield.ModLib;
import com.ae.shield.common.effect.EffectHelper;
import com.ae.shield.common.fluid.FluidHelper;
import com.ae.shield.common.recipe.ModRecipesType;
import com.ae.shield.common.items.EffectStorageItem;
import com.ae.shield.common.items.ItemList;
import com.ae.shield.common.tag.TagHelper;
import net.minecraft.fluid.Fluid;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.network.PacketBuffer;
import net.minecraft.potion.Effect;
import net.minecraft.potion.EffectInstance;
import net.minecraft.tags.FluidTags;
import net.minecraft.tags.ITag;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistryEntry;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;

public class EffectAssemblyRecipe extends AssemblyRecipe {
    private final EffectInstance effect;

    public EffectAssemblyRecipe(ResourceLocation id, EffectInstance effect, Fluid fluid, ITag<Fluid> fluidTag, int fluidCount, int energy, int tick, Ingredient... itemInputs) {
        super(id, new ItemStack(ItemList.EFFECT_STORAGE_ITEM.get()), fluid, fluidTag, fluidCount, energy, tick, itemInputs);
        this.effect = effect;
    }

    @Override
    public boolean matches(@Nonnull IInventory inv, @Nonnull World worldIn) {
        return false;
    }

    @Nonnull
    @Override
    public ItemStack getCraftingResult(@Nonnull IInventory inv) {
        return getRecipeOutput().copy();
    }

    @Nonnull
    @Override
    public ItemStack getRecipeOutput() {
        ItemStack stack = new ItemStack(ItemList.EFFECT_STORAGE_ITEM.get());
        ((EffectStorageItem) stack.getItem()).writePotion(stack, effect);
        return stack;
    }

    public static ResourceLocation getTypeId() {
        return new ResourceLocation(ModLib.MOD_ID, "effect_assembly");
    }

    @Nonnull
    @Override
    public IRecipeSerializer<?> getSerializer() {
        return ModRecipesType.EFFECT_ASSEMBLY_SERIALIZER.get();
    }

    public static class EffectAssemblyRecipeType implements IRecipeType<EffectAssemblyRecipe> {
        @Override
        public String toString() {
            return "effect_assembly";
        }
    }

    public EffectInstance getEffect() {
        return effect;
    }

    public static class Serializer extends ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<EffectAssemblyRecipe> {

        @Nonnull
        @Override
        public EffectAssemblyRecipe read(@Nonnull ResourceLocation id, @Nonnull JsonObject json) {
            EffectInstance effect = EffectHelper.deserializeEffect(JSONUtils.getJsonObject(json, "effect_result"));

            Fluid fluid = null;
            ITag<Fluid> fluidTag = null;
            int fluidCount = 0;

            if (JSONUtils.hasField(json, "fluid_ingredient")) {
                JsonObject fluidJson = JSONUtils.getJsonObject(json, "fluid_ingredient");
                fluid = FluidHelper.deserializeFluid(fluidJson);

                if (fluid == null) {
                    fluidTag = TagHelper.deserializeFluidTag(fluidJson);
                }

                fluidCount = JSONUtils.getInt(fluidJson, "count");
            }

            int energy = JSONUtils.getInt(json, "energy_per_tick");
            int tick = JSONUtils.getInt(json, "processing_time");

            JsonArray ingredients = JSONUtils.getJsonArray(json, "ingredients");
            Ingredient base = Ingredient.deserialize(JSONUtils.getJsonArray(json, "backing"));
            List<Ingredient> inputs = new ArrayList<>();
            inputs.add(base);

            for (JsonElement e : ingredients) {
                inputs.add(Ingredient.deserialize(e));
            }

            return new EffectAssemblyRecipe(id, effect, fluid, fluidTag, fluidCount, energy, tick, inputs.toArray(new Ingredient[0]));
        }

        @Nullable
        @Override
        public EffectAssemblyRecipe read(ResourceLocation id, PacketBuffer buffer) {
            Effect effect = ForgeRegistries.POTIONS.getValue(buffer.readResourceLocation());

            if (effect == null) {
                throw new RuntimeException("无法从包中读取到有效的效果");
            }

            int fluidFlag = buffer.readInt();
            Fluid fluid = null;
            ITag<Fluid> fluidTag = null;

            switch (fluidFlag) {
                case 1:
                    FluidStack fluidStack = buffer.readFluidStack();
                    fluid = fluidStack.getFluid();
                    break;
                case 2:
                    fluidTag = FluidTags.createOptional(buffer.readResourceLocation());
                    break;
            }

            int[] ints = buffer.readVarIntArray(5);
            EffectInstance effectInstance = new EffectInstance(effect, ints[3], ints[4]);
            Ingredient[] inputs = new Ingredient[buffer.readVarInt()];
            for (int i = 0; i < inputs.length; i++) {
                inputs[i] = Ingredient.read(buffer);
            }
            return new EffectAssemblyRecipe(id, effectInstance, fluid, fluidTag, ints[0], ints[1], ints[2], inputs);

        }

        @Override
        public void write(PacketBuffer buffer, EffectAssemblyRecipe recipe) {
            buffer.writeResourceLocation(recipe.effect.getPotion().getRegistryName());

            if (recipe.fluid != null) {
                buffer.writeInt(1);
                buffer.writeFluidStack(new FluidStack(recipe.fluid, 1));
            } else if (recipe.fluidTag != null) {
                buffer.writeInt(2);
                buffer.writeResourceLocation(((ITag.INamedTag<Fluid>) recipe.fluidTag).getName());
            } else {
                buffer.writeInt(0);
            }

            buffer.writeVarIntArray(new int[]{recipe.fluidCount, recipe.energy, recipe.tick, recipe.getEffect().getDuration(), recipe.getEffect().getAmplifier()});
            buffer.writeVarInt(recipe.itemInputs.size());
            for (Ingredient input : recipe.getIngredients()) {
                input.write(buffer);
            }
        }
    }
}
